/**
  ******************************************************************************
  * File Name          : MODBUS protocol
  * Auther             : xifeng.yao@qq.com
  * Description        : Code for modbus applications
  ******************************************************************************
  * @attention
  * 整合3种Modbus数据格式： MODBUS-RTU    MODBUS-TCP    MODBUS-ASCII
  * 对应3个接口函数：
  *
  * @Modbus_RTU:    bool API_ModbusRTU_Slave_Analy(char* protocoldata,  uint16_t* Length);
  * 
  * @Modbus_TCP:    bool API_ModbusTCP_Slave_Analy(char* protocoldata,  uint16_t* Length);
  * 
  * @Modbus_ASCII:  bool API_ModbusASCII_Slave_Analy(char* protocoldata,  uint16_t* Length);
  *   
  ********/
	
/* Includes ------------------------------------------------------------------*/
#include <string.h>

#include "litemodbus.h"
#include "litemodbusBase.h"
#include "litemodbusCrc.h"

//#include "SEGGER_RTT.h"
#define  RTT_printf(...)    printf(__VA_ARGS__)  //SEGGER_RTT_printf(0,__VA_ARGS__)


#ifndef __weak
  #define __weak __attribute__((weak))
#endif

__weak bool Modbus_Coils_Analy(char* data)    {ERR_CMD(data)       return true;}
__weak bool Modbus_Discrete_Analy(char* data) {ERR_CMD(data)       return true;}
__weak bool Modbus_HoldReg_Analy(char* data)  {ERR_CMD(data)       return true;}
__weak bool Modbus_InputReg_Analy(char* data) {ERR_CMD(data)       return true;}

/*Modbus 从机地址*/
static  uint8_t  Modbus_ID[2]/*__attribute__((at(BKPSRAM_BASE + (4<<10) - 8)))*/;
/********************
function:  Modbus 从机地址 【弱声明可重构】
input:     
return:    从机地址指针
*/
__weak uint8_t Modbus_SlaveAddr_Get(void)
{
  /*验证Modbus_ID有效性*/
  if((char)(Modbus_ID[0] + Modbus_ID[1]) != 0x1F) 
  {
    Modbus_ID[0] = 1; //赋初值
    Modbus_ID[1] = 0x1F - Modbus_ID[0]; //校验值
  }
  return Modbus_ID[0];
}
/********************
function:  修改Modbus 从机地址 【弱声明可重构】
input:     
return:    从机地址指针
*/
__weak void Modbus_SlaveAddr_Set(uint8_t addr)
{
  /*验证Modbus_ID有效性*/
  Modbus_ID[0] = addr; //赋初值
  Modbus_ID[1] = 0x1F - addr; //校验值
}

/********************
 @brief:    ModbusRTU 从机模式接收帧解析
 @param:    protocoldata、Length  接收/应答帧 帧长度
 @retval:   true or false (modbus解析正确并完成)
*/
bool API_ModbusRTU_Slave_Analy(char* protocoldata,  uint16_t* Lenght)
{
  uint8_t result;
  uint16_t crc;
  
  if(*Lenght < 8)  //modbus 帧长度不对
  {
    RTT_printf("modbus len wrong\n");
    return false;
  }
  if(((uint8_t)protocoldata[0] != 0) && (protocoldata[0] != Modbus_SlaveAddr_Get()))   //modbus 从设备地址不对   
  {
    RTT_printf("modbus ID wrong\n");
    return false;
  }
  if(!Modbus_CRC16_Judge(protocoldata, *Lenght))   //modbus CRC校验不对 
  {
    RTT_printf("modbus crc wrong\n");
    return false;  //crc 不正确 可能是其他协议帧
  }
  
  result = Modbus_Slave_Analy(protocoldata, Lenght);
  
  if(result)
  { //解析正确 添加CRC校验
    crc = Modbus_CRC16_Calculate(protocoldata, *Lenght); //crc校验
    memcpy(&protocoldata[*Lenght], &crc, 2); 
    *Lenght += 2; //CRC校验 2字节长度
  }
  return result;
}

/*------------------------------------------------------------------------------------------*/
/********************
 @brief:    ModbusTCP 从机模式接收帧解析
 @param:    protocoldata、Length  接收/应答帧 帧长度
 @retval:   true or false (modbus解析正确并完成)
*/
bool API_ModbusTCP_Slave_Analy(char* protocoldata,  uint16_t* Lenght)
{
  uint8_t result;
  
  *Lenght = HSB2LSB((uint8_t*)&protocoldata[4], 16);
  
  if(((uint8_t)protocoldata[0] != 0) && (protocoldata[0] != Modbus_SlaveAddr_Get()))   //modbus 从设备地址不对   
  {
    RTT_printf("modbus ID wrong\n");
    return false;
  }
  
  result = Modbus_Slave_Analy(protocoldata+6, Lenght);
  
  if(result)
  { //解析正确 修改回帧长度
    protocoldata[4] = *Lenght >> 4;     //TCP协议帧头 应答长度
    protocoldata[5] = *Lenght & 0x00ff; //TCP协议帧头 应答长度
    *Lenght += 6;                       //TCP协议多6字节
  }
  return result;
}

/*------------------------------------------------------------------------------------------*/
/**
  * @brief  ASCII HEX 字符相互转换
  * @param  
  * @retval true or false
  */ 
extern bool ASCII2HEX(char* input, uint16_t* lenght);
extern bool HEX2ASCII(char* input, uint16_t* lenght);
/**
  * @brief  求累加和
  * @param  
  * @retval true or false
  */ 
extern uint8_t SUM_int8(char* input, uint16_t lenght);

/********************
 @brief:    ModbusASCII 从机模式接收帧解析
 @param:    protocoldata、Length  接收/应答帧 帧长度
 @retval:   true or false (modbus解析正确并完成)
*/
bool API_ModbusASCII_Slave_Analy(char* protocoldata,  uint16_t* Lenght)
{
  uint8_t result;
  
  if(*protocoldata != ':')      {return false;}  //非 Modbus ASCII 协议
  if(false == ASCII2HEX(protocoldata+1, Lenght))  {return false;} // Modbus ASCII 协议转译
  if(((uint8_t)protocoldata[1] != 0) && (protocoldata[1] != Modbus_SlaveAddr_Get()))   //modbus 从设备地址不对   
  {
    RTT_printf("modbus ID wrong\n");
    return false;
  }
  
  /*累加和校验*/
  if(SUM_int8(protocoldata+1, *Lenght) != 0) {return false;}   //校验和不对
    
  /*协议解析*/
  result = Modbus_Slave_Analy(protocoldata+1, Lenght);
  
  if(result)
  { //解析正确 将hex 转 ascii
    protocoldata[1 + *Lenght] = SUM_int8(protocoldata+1, *Lenght); //计算校验和
    HEX2ASCII(protocoldata+1, Lenght); //hex 转 ascii
  }
  return result;
}
